﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
using System.Text.RegularExpressions;
using XemlUtilities.ISA;
using XemlUtilities.ISA.Ontology;
using XemlUtilities.DataSet;

namespace XemlUtilities.ISA
{
    //arghhhh negative example
    //ftp://ftp.ebi.ac.uk/pub/databases/bii/submission_repo/study_BII-S-1_eG7vJlIOg8/s_BII-S-1.txt

    class Study : IsaTable
    {
        //NCBITaxon:3702
        private static Regex rxNCBITaxon = new Regex(@"(NCBITaxon:\d+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

        #region constructors
        private Study(XemlDataSet xds)
            : base(xds)
        {
            DataColumn dc = new DataColumn("Source Name", typeof(string));
            dc.AllowDBNull = false;
            this.Columns.Add(dc);

            foreach (DataRow item in this.Rows)
            {
                int SampleId = GetSampleId(item);
                item[dc] = xds.SampleMapping.Where(x => x.FK_Sample == SampleId).Select(x => x.GetReplicateDescription()).Distinct().Single();
            }

            #region do the Material stuff
            DataColumn tmpCol = new DataColumn("Characteristics[organism part]", typeof(string));
            DataColumn tmpCol1 = this.CreateTermSourceREF();
            DataColumn tmpCol2 = this.CreateTermAccessionNumber();
            this.Columns.AddRange(new DataColumn[] { tmpCol, tmpCol1, tmpCol2 });

            foreach (DataRow sample in this.Rows)
            {
                int sampleId = this.GetSampleId(sample);
                XemlDataSet.SampleRow sampleRow = xds.Sample.Single(x => x.Id == sampleId);

                var materials = xds.Partition.Where(x => x.FK_Sample == sampleRow.Id).Join(xds.Material, p => p.Id, m => m.FK_Partition, (p, m) => m);
                var terms = materials.Join(xds.Term, m => m.FK_Term, t => t.Id, (m, t) => t);
                List<OntologyBase> ontologies = new List<OntologyBase>();
                foreach (String ontologyAcronym in terms.Select(x => (x.Table.DataSet as XemlDataSet).Ontology.Single(y => y.Id == x.FK_Ontology).GetProperNameSpaceAlias()).Distinct().ToList())
	            {
                    var o = OntologyBase.GetOrCreateOntology(ontologyAcronym);
                    if (!ontologies.Contains(o))
                    {
                        ontologies.Add(o);
                    }
                }

                sample[tmpCol] = string.Join(IsaExporter.multipleValueDelimiter, terms.Select(x => x.Name).Distinct());
                sample[tmpCol1] = string.Join(IsaExporter.multipleValueDelimiter, ontologies.Select(x => x.Acronym).ToList());
                sample[tmpCol2] = string.Join(IsaExporter.multipleValueDelimiter, terms.Select(x => x.TermId).Distinct());
            }
            #endregion do the Material stuff

            #region do the GermPlasm stuf
            //tmpCol = new DataColumn("Characteristics[germplasm]", typeof(string));
            tmpCol = new DataColumn("Characteristics[Organism]", typeof(string));
            tmpCol1 = this.CreateTermSourceREF();
            tmpCol2 = this.CreateTermAccessionNumber();
            this.Columns.AddRange(new DataColumn[] { tmpCol, tmpCol1, tmpCol2 });

            OntologyBase EFOntology = OntologyBase.GetOrCreateOntology("EFO");

            foreach (DataRow sample in this.Rows)
            {
                int sampleId = this.GetSampleId(sample);
                XemlDataSet.SampleRow sampleRow = xds.Sample.Single(x => x.Id == sampleId);
                List<XemlDataSet.ObservationRow> ObPoints = sampleRow.GetObservations().Distinct().ToList();

                IEnumerable<string> NcbiTaxonomyIds = ObPoints.Select(x => x.IndividualsPoolRowParent).Where(x => !x.IsAnnotationIdNull())
                    .Select(x => xds.Annotation.Single(y => y.Id == x.AnnotationId && y.Tag == "NcbiTaxonomyId"))
                    .Select(x => x.Annotation)
                    .Distinct()
                    .OrderBy(x => x)
                    .Select(x => string.Concat("NCBITaxon:", x));

                sample[tmpCol] = string.Join(IsaExporter.multipleValueDelimiter, ObPoints.Select(x => x.IndividualsPoolRowParent.GermPlasm).Distinct());
                sample[tmpCol1] = EFOntology.Acronym; // needs to match an ontogy from OLS
                sample[tmpCol2] = string.Join(IsaExporter.multipleValueDelimiter, NcbiTaxonomyIds);
            }
            #endregion do the GermPlasm stuff

            tmpCol = new DataColumn("Protocol REF", typeof(string));
            this.Columns.Add(tmpCol);
            foreach (DataRow sample in this.Rows)
            {
                sample[tmpCol] = "growth protocol";// "Sample collection";
            }

            #region do the XeO stuff

            Protocol.Growth GrowthProtocoll = new Protocol.Growth(xds);

            IEnumerable<Protocol.Growth.GrowthStep> gs = GrowthProtocoll.GetGrowthSteps();
            foreach (Protocol.Growth.GrowthStep f in GrowthProtocoll.GetGrowthSteps())
            {
                String caption = String.Format("Factor Value [{0}]", f.ToString());
                int captionCount = this.GetColumns().Count(x => x.Caption == caption);
                String header = string.Concat(caption, captionCount);
                tmpCol = new DataColumn(header, typeof(string));
                tmpCol.Caption = caption;
                DataColumn dcUnit = this.CreateUnit();
                tmpCol1 = this.CreateTermAccessionNumber();
                tmpCol2 = this.CreateTermSourceREF();
                this.Columns.AddRange(new DataColumn[] { tmpCol, dcUnit, tmpCol2, tmpCol1 });

                //Lookup up the actuall values describing the experimental setup
                foreach (DataRow sample in this.Rows)
                {
                    int sampleId = this.GetSampleId(sample);
                    // Multiple values can be returned and which only happens when the conditions are the same.
                    // This only happens when also the captions of the columns are the same.
                    XemlDataSet.ValueRow latestValue = xds.Value.GetLatestValues(xds.Sample.Single(x => x.Id == sampleId).FK_Story, f.id, f.time.Value)[captionCount];

                    if (latestValue != null)
                    {
                        sample[tmpCol] = latestValue.Value;

                        // TODO: remove this quote-removing once the XEML parser is able to remove redundant quotes
                        if (!latestValue.IsUnitNull())
                        {
                            sample[dcUnit] = latestValue.Unit.Replace("\"", string.Empty);
                        }
                        sample[tmpCol1] = f.term.AccessionNr;
                        sample[tmpCol2] = f.term.Ontology.Acronym;
                    }
                }
            }
            #endregion do the XeO stuff

            #region smallmolecule_abundance_std_error_sub
            DataColumn abundanceStdError = new DataColumn("smallmolecule_abundance_std_error_sub");
            #endregion smallmolecule_abundance_std_error_sub

            #region do the Sample stuff
            tmpCol = new DataColumn("Sample Name");
            this.Columns.Add(tmpCol);

            //Lookup up the actuall values describing the experimental setup
            foreach (DataRow sample in this.Rows)
            {
                int sampleId = this.GetSampleId(sample);
                XemlDataSet.SampleMappingRow sm = xds.SampleMapping.SingleOrDefault(x => x.FK_Sample == sampleId && x.SubKey == "Chromatogram");

                if (sm != null)
                {
                    sample[tmpCol] = sm.Value;
                }
            }
            #endregion do the Sample stuff
        }
        #endregion constructors

        public static MemoryStream GetStream(XemlDataSet xds)
        {
            using (Study s = new Study(xds))
            {
                MemoryStream res = new MemoryStream();
                s.Write(res);
                res.Seek(0, SeekOrigin.Begin);
                return res;
            }
        }

        public static string GetFileName(XemlDataSet xds)
        {
            return string.Format("s_{0}.txt", xds.Document.Single().PutativeFileName);
        }
    }
}
